package com.wzcl.app.service.impl;

import com.alibaba.fastjson.JSON;
import com.wzcl.app.dao.model.Activity;
import com.wzcl.app.dao.model.ActivitySeckillPromotion;
import com.wzcl.app.dao.model.Commodity;
import com.wzcl.app.dao.model.common.Base;
import com.wzcl.app.dao.model.common.Promotion;
import com.wzcl.app.dao.repository.CommodityRepository;
import com.wzcl.app.model.common.BusinessException;
import com.wzcl.app.model.common.CodeMsg;
import com.wzcl.app.model.common.MyPage;
import com.wzcl.app.model.common.WZCL;
import com.wzcl.app.model.dto.ActivityDto;
import com.wzcl.app.model.dto.CommodityDto;
import com.wzcl.app.service.ActivityService;
import com.wzcl.app.service.CommodityService;
import com.wzcl.app.service.CourseService;
import com.wzcl.app.service.MaterialService;
import com.wzcl.app.utils.CollectionUtil;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.criteria.*;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

@Log4j2
@Service
@Transactional
public class CommodityServiceImpl implements CommodityService {

    @Autowired
    private CommodityRepository repository;

    @Autowired
    private CourseService courseService;

    @Autowired
    private MaterialService materialService;

    @Autowired
    private ActivityService activityService;

    @Override
    public Commodity  addCommodity(CommodityDto commodityDto) {
        //校验商品资源
        getResource(commodityDto);
        //创建商品
        Commodity commodity = new Commodity();
        BeanUtils.copyProperties(commodityDto, commodity, "id", "currentPrice", "oldPrice", "pictures", "sale");
        commodity.setCurrentPrice(new BigDecimal(commodityDto.getCurrentPrice()));
        commodity.setOldPrice(new BigDecimal(commodityDto.getOldPrice()));
        if (commodityDto.getPictures() != null && commodityDto.getPictures().size() > 0) {
            commodity.setPictures(StringUtils.join(commodityDto.getPictures(), ","));
        }
        commodity.setSale(false);
        commodity.setSpec(false);
        commodity.setState(WZCL.STATE.NOT_DELETE);
        return repository.save(commodity);
    }

    @Override
    public void delCommodityByResource(Integer resourceId, Integer resourceType) {
        log.info("Commodity logic deletion By resourceId:{} And resourceType:{}", resourceId, resourceType);
        repository.deleteByResourceIdAndType(resourceId, resourceType);
    }

    @Override
    public Commodity updateCommodity(CommodityDto commodityDto) {
        //校验
        Commodity commodity = getAndCheckById(commodityDto);
        //修改
        BeanUtils.copyProperties(commodityDto, commodity, "id", "resourceId", "type", "oldPrice", "currentPrice", "pictures", "sale");
        if (StringUtils.isNotBlank(commodityDto.getOldPrice())) {
            commodity.setOldPrice(new BigDecimal(commodityDto.getOldPrice()));
        }
        if (StringUtils.isNotBlank(commodityDto.getCurrentPrice())) {
            commodity.setCurrentPrice(new BigDecimal(commodityDto.getCurrentPrice()));
        }
        if (commodityDto.getPictures() != null && commodityDto.getPictures().size() > 0) {
            commodity.setPictures(StringUtils.join(commodityDto.getPictures(), ","));
        }
        return repository.save(commodity);
    }

    @Override
    public Commodity getCommodityById(Integer id) {
        return repository.findById(id).orElse(null);
    }

    //获取商品绑定的资源
    @Override
    public Base getResource(CommodityDto commodityDto) {
        if (commodityDto == null || commodityDto.getResourceId() == null) {
            throw new BusinessException(CodeMsg.MUST_BE_NOT_NULL, "缺少资源ID");
        }
        Base base = null;
        if (WZCL.COMMODITY_TYPE.COURSE.equals(commodityDto.getType())) {
            base = courseService.getCourseById(commodityDto.getResourceId());
        } else if (WZCL.COMMODITY_TYPE.MATERIAL.equals(commodityDto.getType())) {
            base = materialService.getMaterialById(commodityDto.getResourceId());
        } else {
            throw new BusinessException(CodeMsg.COMMODITY_TYPE_ERROR);
        }
        if (base == null) {
            throw new BusinessException(CodeMsg.COMMODITY_RESOURCE_NOT_EXIST);
        }
        return base;
    }

    @Override
    public List<Commodity> getCommodityList(CommodityDto commodityDto) {
        return repository.findAll(getSpecification(commodityDto), commodityDto.getSort());
    }

    @Override
    public List<Commodity> getCommodityListByIds(List<Integer> commodityIdList) {
        return repository.findAllById(commodityIdList);
    }

    @Override
    public MyPage<Commodity> getCommodityPage(CommodityDto commodityDto) {
        List<Commodity> all = repository.findAll(getSpecification(commodityDto));
        ArrayList<Commodity> list = new ArrayList<>(all);
        List<Commodity> listBySort = CollectionUtil.getListBySort(list, "getUpdateTime", "desc");
        List<Commodity> listByPage = CollectionUtil.getListByPage(listBySort, commodityDto.getPageNumber()+1, commodityDto.getPageSize());
        MyPage<Commodity> myPage = new MyPage<>();
        myPage.setContent(listByPage);
        myPage.setTotalElements(list.size());
        return myPage;
    }

    /**
     * 上架商品
     *
     * @param id
     * @return
     */
    @Override
    public Commodity saleCommodity(Integer id) {
        //校验获取
        Commodity commodity = getAndCheckById(id);
        //上架
        if (!commodity.getSale()) {
            commodity.setSale(true);
            commodity.setSaleTime(new Date());
            log.info("Commodity ID:{} on sale", id);
            commodity = repository.save(commodity);
            //上线时绑定课程/物品
            if (WZCL.COMMODITY_TYPE.COURSE.equals(commodity.getType())) {
                courseService.addCourseCommodity(commodity);
            } else if (WZCL.COMMODITY_TYPE.MATERIAL.equals(commodity.getType())) {
                materialService.addMaterialCommodity(commodity);
            }
            return commodity;
        }
        return commodity;
    }


    /**
     * 下架商品
     *
     * @param id
     * @return
     */
    @Override
    public Commodity unSaleCommodity(Integer id) {
        //校验获取
        Commodity commodity = getAndCheckById(id);
        //下架
        if (commodity.getSale()) {
            commodity.setSale(false);
            commodity.setSaleTime(null);
            log.info("Commodity ID:{} off sale", id);
            commodity = repository.save(commodity);
            //下线时解绑课程/物品
            if (WZCL.COMMODITY_TYPE.COURSE.equals(commodity.getType())) {
                courseService.delCourseCommodityById(commodity.getResourceId());
            } else if (WZCL.COMMODITY_TYPE.MATERIAL.equals(commodity.getType())) {
                materialService.delMaterialCommodityById(commodity.getResourceId());
            }
            return commodity;
        }
        return commodity;
    }

    @Override
    public Commodity bindActivity(CommodityDto commodityDto) {
        //校验获取
        Commodity commodity = getAndCheckById(commodityDto);
        //校验绑定数据
        List<Integer> activityIdList = commodityDto.getActivityIdList();
        if (commodityDto.getIsBind() == null || activityIdList == null || activityIdList.size() == 0) {
            throw new BusinessException(CodeMsg.MUST_BE_NOT_NULL, "绑定数据缺失");
        }
        //当前商品已有的活动
        List<Activity> activityList = commodity.getActivityList();
        //是否有秒杀活动
        boolean hasSeckill = false;
        if (commodityDto.getIsBind()) {
            //绑定前先处理已存在的
            if (activityList != null && activityList.size() > 0) {
                for (Activity activity : activityList) {
                    if (!hasSeckill && WZCL.ACTIVITY_TYPE.SECKILL.equals(activity.getType())) {
                        hasSeckill = true;
                    }
                    if (activityIdList.contains(activity.getId())) {
                        log.info("Commodity ID:{} has bind this Activity ID:{},pass bind.", commodity.getId(), activity.getId());
                        activityIdList.remove(activity.getId());
                    }
                }
            }
            //对数据校验
            if (activityIdList.size() == 0) {
                log.info("Commodity ID:{} has no Activity to bind", commodity.getId());
                throw new BusinessException(CodeMsg.COMMODITY_BIND_FAIL, "无可绑定活动");
            }
            //绑定
            List<Activity> activity = activityService.getActivityById(activityIdList);
            if (activity != null) {
                //已上线的活动不让解绑和绑定
                activity = activity.stream().filter(o -> !WZCL.ACTIVITY_STATE.ONLINE.equals(o.getActivityState())).collect(Collectors.toList());
                if (activity.size() > 0) {
                    //校验秒杀活动的唯一性(1.原有秒杀+新增秒杀/2.新增多个秒杀)
                    for (Activity var1 : activity) {
                        if (hasSeckill && WZCL.ACTIVITY_TYPE.SECKILL.equals(var1.getType())) {
                            throw new BusinessException(CodeMsg.COMMODITY_BIND_FAIL, "秒杀活动必须唯一");
                        } else if (WZCL.ACTIVITY_TYPE.SECKILL.equals(var1.getType())) {
                            //先扣除库存
                            List<Promotion> promotionList = activityService.getFactoryByType(var1.getType()).findAllPromotion(var1.getId());
                            if (promotionList == null || promotionList.size() == 0) {
                                throw new BusinessException(CodeMsg.ACTIVITY_STATE_ERROR, "缺少秒杀活动内容");
                            }
                            if (commodity.getStock() != null) {
                                ActivitySeckillPromotion seckillPromotion = (ActivitySeckillPromotion) promotionList.get(0);
                                if (commodity.getStock() >= seckillPromotion.getStock()) {
                                    commodity.setStock(commodity.getStock() - seckillPromotion.getStock());
                                } else {
                                    throw new BusinessException(CodeMsg.COMMODITY_BIND_FAIL, "商品的库存不足，无法设置秒杀活动");
                                }
                            }
                            hasSeckill = true;
                        }
                    }
                    log.info("Commodity ID:{} bind  ActivityList {}", commodity.getId(), JSON.toJSONString(activity));
                    activityList.addAll(activity);
                } else {
                    throw new BusinessException(CodeMsg.COMMODITY_BIND_FAIL, "无可绑定活动");
                }
            } else {
                throw new BusinessException(CodeMsg.COMMODITY_BIND_FAIL, "无可绑定活动");
            }
            commodity.setActivityList(activityList);
            return repository.save(commodity);
        } else {
            //解绑
            if (activityList != null && activityList.size() > 0) {
                int size = activityList.size();
                //已上线的活动不让解绑和绑定
                List<Activity> collect = activityList.stream()
                        .filter(activity -> {
                            for (Integer integer : activityIdList) {
                                if (activity.getId().equals(integer) && !WZCL.ACTIVITY_STATE.ONLINE.equals(activity.getActivityState())) {
                                    return false;
                                }
                            }
                            return true;
                        })
                        .collect(Collectors.toList());
                //校验数据
                if (size == collect.size()) {
                    throw new BusinessException(CodeMsg.COMMODITY_BIND_FAIL, "无可解绑活动");
                }
                commodity.setActivityList(collect);
                return repository.save(commodity);
            } else {
                throw new BusinessException(CodeMsg.COMMODITY_BIND_FAIL, "无可解绑活动");
            }
        }
    }

    @Override
    public Commodity getAndCheckCommodityById(Integer commodityId) {
        Commodity commodity = getAndCheckById(commodityId);
        if (!commodity.getSale()) {
            throw new BusinessException(CodeMsg.COMMODITY_NOT_SALE);
        }
        if (commodity.getStock() != null && commodity.getStock() == 0) {
            throw new BusinessException(CodeMsg.COMMODITY_COUNT_SHOP, "库存不足");
        }
        return commodity;
    }

    @Override
    public Commodity getAndCheckById(Integer id) {
        if (id == null) {
            throw new BusinessException(CodeMsg.MUST_BE_NOT_NULL, "缺少商品ID");
        }
        Commodity commodity = getCommodityById(id);
        if (commodity == null) {
            throw new BusinessException(CodeMsg.COMMODITY_NOT_EXIST);
        }
        if (commodity.getState().equals(WZCL.STATE.IS_DELETE)) {
            throw new BusinessException(CodeMsg.COMMODITY_IS_DELETE);
        }
        return commodity;
    }

    //减少商品库存/增加销量
    @Override
    public void decreaseStock(Commodity... commodities) {
        List<Commodity> commodityList = new ArrayList<>();
        for (Commodity commodity : commodities) {
            if (commodity.getStock() != null) {
                commodity.setStock(commodity.getStock() - 1);
                commodityList.add(commodity);
            }
            if (commodity.getSold() != null) {
                commodity.setSold(commodity.getSold() + 1);
            } else {
                commodity.setSold(1);
            }
        }
        repository.saveAll(commodityList);
    }

    //恢复库存
    @Override
    public void increaseStock(Commodity... commodities) {
        List<Commodity> commodityList = new ArrayList<>();
        for (Commodity commodity : commodities) {
            if (commodity.getStock() != null) {
                commodity.setStock(commodity.getStock() + 1);
                commodityList.add(commodity);
            }
        }
        repository.saveAll(commodityList);
    }

    @Override
    public void check(Commodity commodity) {
        if (!commodity.getSale()) {
            throw new BusinessException(CodeMsg.COMMODITY_NOT_SALE);
        }
        if (commodity.getStock() != null && commodity.getStock() == 0) {
            throw new BusinessException(CodeMsg.COMMODITY_COUNT_SHOP, "库存不足");
        }
    }

    private Specification<Commodity> getSpecification(CommodityDto commodityDto) {
        return new Specification<Commodity>() {
            @Override
            public Predicate toPredicate(Root<Commodity> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                List<Predicate> predicates = new ArrayList<Predicate>();
                if (commodityDto.getType() != null) {
                    predicates.add(criteriaBuilder.equal(root.get("type"), commodityDto.getType()));
                }
                if (commodityDto.getResourceId() != null) {
                    predicates.add(criteriaBuilder.equal(root.get("resourceId"), commodityDto.getResourceId()));
                }
                if (commodityDto.getSale() != null) {
                    predicates.add(criteriaBuilder.equal(root.get("sale"), commodityDto.getSale()));
                }
                if (commodityDto.getRecommend() != null) {
                    predicates.add(criteriaBuilder.equal(root.get("recommend"), commodityDto.getRecommend()));
                }
                if (commodityDto.getNeedDelivery() != null) {
                    predicates.add(criteriaBuilder.equal(root.get("needDelivery"), commodityDto.getNeedDelivery()));
                }
                if (StringUtils.isNotBlank(commodityDto.getName())) {
                    predicates.add(criteriaBuilder.like(root.get("name"), "%" + commodityDto.getName() + "%"));
                }
                ActivityDto activityDto = commodityDto.getActivityDto();
                if (activityDto != null) {
                    Join<Commodity, Activity> activityPath = root.join(root.getModel().getList("activityList", Activity.class), JoinType.LEFT);
                    if (activityDto.getActivityState() != null) {
                        predicates.add(criteriaBuilder.equal(activityPath.get("activityState"), activityDto.getActivityState()));
                    }
                    if (activityDto.getType() != null) {
                        predicates.add(criteriaBuilder.equal(activityPath.get("type"), activityDto.getType()));
                    }
                }
                predicates.add(criteriaBuilder.equal(root.get("state"), WZCL.STATE.NOT_DELETE));
                return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
            }
        };
    }
}
